package discov

import (
	"fmt"
	"math/rand"
	"sync"

	"github.com/hashicorp/consul/api"
	"github.com/pkg/errors"
)

func NewConsulDiscover(conf Conf) (Discover, error) {
	config := api.DefaultConfig()
	config.Address = conf.Host
	client, err := api.NewClient(config)
	if err != nil {
		return nil, errors.Wrap(err, "consul client error")
	}

	return &ConsulDiscover{
		conf:   conf,
		consul: client,
	}, nil
}

type ConsulDiscover struct {
	conf      Conf
	consul    *api.Client
	endpoints []string
}

func (d *ConsulDiscover) GetService() error {
	instances, _, err := d.consul.Health().Service(d.conf.ServiceName, "", true, &api.QueryOptions{
		UseCache: true,
	})
	if err != nil {
		return errors.Wrap(err, "consul get service error")
	}

	var endpoints []string
	for _, item := range instances {

		var (
			schema    string = "http://"
			prefixApi string = ""
			address   string = item.Service.Address
			port      int    = item.Service.Port
			meta             = item.Service.Meta
		)

		if secure, ok := meta["secure"]; ok {
			if secure == "true" {
				schema = "https://"
			}
		}

		if prefix, ok := meta["contextPath"]; ok {
			prefixApi = prefix
		}
		path := fmt.Sprintf("%s%s:%d%s", schema, address, port, prefixApi)
		endpoints = append(endpoints, path)
	}

	d.endpoints = endpoints
	return nil
}

func (d *ConsulDiscover) Endpoint() (string, error) {
	if err := d.GetService(); err != nil {
		return "", err
	}
	return d.roundRobin()
}
func (d *ConsulDiscover) roundRobin() (string, error) {
	var i = rand.Int()
	var mtx sync.Mutex

	if len(d.endpoints) == 0 {
		return "", NodeNotFoundErr
	}
	mtx.Lock()
	node := d.endpoints[i%len(d.endpoints)]
	i++
	mtx.Unlock()
	return node, nil
}
